﻿using System;
using System.Drawing;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace TouchUI
{
    public class ScrollControl : Control
    {
        public int ScrollTop { get; set; }
        public int ScrollHeight { get; set; }
        public bool PointSelected { get; set; }
        public int HorizontalGesture { get; set; }

        private Point mouseDownPoint;
        private Point previousPoint = Point.Empty;
        private bool mouseIsDown = false;
        private int draggedDistance = 0;
        private bool renderLockFlag = false;
        private float snapBackFactor = 0.4f; //0.2f;
        private float velocity = 0.0f;
        private float dragDistanceFactor = 50.0f;
        private float maxVelocity = 500.0f;
        private float deaccelerationFactor = 0.9f;
        private float borderDeaccelerationFactor = 0.5f; // 0.9f;
        private float elapsedTime = 0.08f; // 80 / 1000;

        public ScrollControl()
        {
            ScrollTop = 0;
            ScrollHeight = 0;
            PointSelected = false;
        }

        public override void MouseDown(Point p)
        {
            mouseIsDown = true;
            mouseDownPoint = p;
            previousPoint = mouseDownPoint;
        }

        public override void MouseMove(Point p)
        {
            if(!mouseIsDown)
                return;

            if(!renderLockFlag)
            {
                renderLockFlag = true;
                int delta = p.Y - previousPoint.Y;
                draggedDistance = delta;
                scroll(delta);
                previousPoint = p;
            }
        }

        public override void MouseUp(Point p)
        {
            if(!mouseIsDown)
                return;

            if(renderLockFlag)
            {
                velocity = Math.Min(Math.Max(dragDistanceFactor * draggedDistance, -maxVelocity), maxVelocity);
                draggedDistance = 0;
                DoAutomaticMotion();
            }
            PointSelected = (Math.Abs(p.Y - mouseDownPoint.Y) < 5);
            if(Math.Abs(mouseDownPoint.X - p.X) > 50)
                HorizontalGesture = mouseDownPoint.X - p.X;
            else
                HorizontalGesture = 0;
            mouseIsDown = false;
        }


        public virtual void DoAutomaticMotion()
        {
            renderLockFlag = false;

            if(!mouseIsDown)
            {
                velocity *= deaccelerationFactor;
                if(ScrollTop > 0 || ScrollTop + ScrollHeight < Rectangle.Height)
                    velocity *= borderDeaccelerationFactor;
                float deltaDistance = elapsedTime * velocity;
                if(Math.Abs(deltaDistance) >= 1.0f)
                    scroll((int)deltaDistance);
                else
                {
                    if(ScrollTop != 0)
                    {
                        if(ScrollTop > 0)
                            scroll(-Math.Max(1, (int)(snapBackFactor * (float)(ScrollTop))));
                        else
                        {
                            if(ScrollHeight > Rectangle.Height)
                            {
                                int bottomPosition = ScrollTop + ScrollHeight + Rectangle.Top;
                                if(bottomPosition < Rectangle.Height)
                                    scroll(Math.Max(1, (int)(snapBackFactor * (float)(Rectangle.Height - bottomPosition))));
                            }
                            else
                                scroll(Math.Max(1, -((int)(snapBackFactor * (float)ScrollTop))));
                        }
                    }
                }
            }
        }

        private void scroll(int offset)
        {
            if(offset == 0)
                return;

            ScrollTop += offset;
        }
    }
}
